/*
 * @Author: your name
 * @Date: 2021-11-03 14:38:23
 * @LastEditTime: 2021-11-04 17:41:44
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \vue-source-code-snanndom\src\mySnabbdom\updateChildren.js
 */
import createElement from "./createElement";
import patchVnode from "./patchVnode";

/**
 * @name: 判断两个节点的sel和key是否完全相等，是否是同一个虚拟节点
 */
function checkSameVnode(a, b) {
    return a.sel == b.sel && a.key == b.key
}

/**
 * @name: diff算法核心部分，判断新老children内部的规律，并对老的子节点做移动，新增，删除的操作
 * @param {*} parentElm：父节点，用于移动时做一个标杆
 * @param {*} oldCh：老的子元素
 * @param {*} newCh：新的子元素
 */
export default function updateChildren(parentElm, oldCh, newCh) {
    console.log('新老子节点（children）的判断，老节点children：', oldCh, '新节点children：', newCh);

    // 旧前
    let oldStartIdx = 0;
    // 新前
    let newStartIdx = 0;
    // 旧后
    let oldEndIdx = oldCh.length - 1;
    // 新后
    let newEndIdx = newCh.length - 1;

    // 旧前节点
    let oldStartVnode = oldCh[0]
    // 旧后节点
    let oldEndVnode = oldCh[oldEndIdx]
    // 新前节点
    let newStartVnode = newCh[0]
    // 新后节点
    let newEndVnode = newCh[newEndIdx]

    let keyMap = null;

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        // 首先不是判断diff算法1，2，3，4结果命中，而是要掠过已经加undefined标记的东西
        if (!oldStartVnode || !oldCh[oldStartIdx]) {
            oldStartVnode = oldCh[++oldStartIdx]
        } else if (!oldEndVnode || !oldCh[oldEndIdx]) {
            oldEndVnode = oldCh[--oldEndIdx]
        } else if (!newStartVnode || !newCh[newStartIdx]) {
            newStartVnode = newCh[++newStartIdx]
        } else if (!newEndVnode || !newCh[newEndIdx]) {
            newEndVnode = newCh[--newEndIdx]
        } else if (checkSameVnode(oldStartVnode, newStartVnode)) {
            console.log('1.新前和旧前');
            patchVnode(oldStartVnode, newStartVnode)
            // 新前和旧前后移
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
        } else if (checkSameVnode(oldEndVnode, newEndVnode)) {
            console.log('2.新后和旧后');
            patchVnode(oldEndVnode, newEndVnode)
            // 新后和旧后前移
            oldEndVnode = oldCh[--oldEndIdx]
            newEndVnode = newCh[--newEndIdx]
        } else if (checkSameVnode(oldStartVnode, newEndVnode)) {
            console.log('3.新后和旧前');
            patchVnode(oldStartVnode, newEndVnode)
            // 当3.新后与旧前命中的时候，此时要移动节点。移动新后指定的这个节点到老节点的旧后后面
            parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
            // 新后前移，旧前后移
            oldStartVnode = oldCh[++oldStartIdx]
            newEndVnode = newCh[--newEndIdx]

        } else if (checkSameVnode(newStartVnode, oldEndVnode)) {
            console.log('4.旧后和新前');
            patchVnode(oldEndVnode, newStartVnode)
            // 当4.新前与旧后命中的时候，此时要移动节点。移动新前指定的这个节点到老节点的旧前的前面
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
            // 旧后前移，新前后移
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
        } else {
            // 四种命中都没有命中
            // 制作keyMap一个映射对象，这样就不用每次都遍历对象了
            if (!keyMap) {
                keyMap = {}
                // 从 oldStartIdx开始，到oldEndIdx结束，制作keyMap映射对象
                for (let i = oldStartIdx; i <= oldEndIdx; i++) {
                    const key = oldCh[i].key;
                    if (key != undefined) {
                        keyMap[key] = i
                    }
                }
            }
            // 寻找当前这一项（newStartIdx）这项在可以Mao中的映射的位置序号
            const idxIndex = keyMap[newStartVnode.key]
            if (idxIndex) {
                // 判断,老节点如果idxIndex存在，表示前面4个条件都不满足的情况下，在老节点中存在新节点指针指向的节点 - 移动位置即可
                const elmToMove = oldCh[idxIndex]
                console.log('四种命中都不满足，该新节点在老节点数组中存在-移动：', elmToMove);
                patchVnode(elmToMove, newStartVnode)
                // 把这判断为undefined，表示我这一项走完了
                oldCh[idxIndex] = undefined
                // 调用insertBefore移动 移动的项是dom节点
                parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm)

            } else {
                // 判断，老节点不存在这一项，则在老节点中更新（新增这一项），在老前指针前面添加 - 添加的项不是dom节点
                console.log('四种命中都不满足，该新节点在老节点数组中不存在-新增：', newStartVnode);
                parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm)
            }

            // 指针下移，之移动新的头
            newStartVnode = newCh[++newStartIdx]
        }
    }

    // 继续看看有没有剩余的
    if (newStartIdx <= newEndIdx) {
        // 循环结束了，start还是比old小
        console.log('新节点还有剩余子节点没有处理');
        // 插入的标杆
        // const before = newCh[newEndIdx + 1] ? newCh[newEndIdx + 1].elm : null;

        for (let i = newStartIdx; i <= newEndIdx; i++) {
            // insertBefore方法可以自动识别null,如果是null，就会自动拍到队尾去，和appendChild是一直了
            // newCh[i]现在还没有真的dom，所以调用createElement()变为dom
            console.log('new剩余节点：', newCh[i]);
            parentElm.insertBefore(createElement(newCh[i]), oldCh[oldStartIdx])
        }
    } else if (oldStartIdx <= oldEndIdx) {
        console.log('old还有剩余节点没有处理')
        // 批量删除oldStart和oldend指针之间的项
        for (let i = oldStartIdx; i <= oldEndIdx; i++) {
            if (oldCh[i]) {
                console.log('old剩余节点：', oldCh[i]);
                parentElm.removeChild(oldCh[i].elm)
            }
        }
    }

}